<?php
namespace Piz;
class Server
{
    private static $instance;
    public $server ;

    private $config ;
    private $workerId;
    public $name ;
    private function __construct (){}
    private function __clone() {}

    public static function get_instance(){
        if(is_null (self::$instance)){
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function set_config($config){
        $this->config = $config;
        $this->name = $config['name'];
    }

    public function run(){
        $swoole_server = isset($this->config['server']) && $this->config['server'] == 'websocket' ? 'swoole_websocket_server' : 'swoole_http_server';
        $this->config['ip'] = $ip = isset($this->config['ip']) && ip2long($this->config['ip']) ? $this->config['ip'] :'0.0.0.0';
        $this->config['port'] = $port = isset($this->config['port']) && intval($this->config['port']) ? $this->config['port'] : 9527 ;
        $this->server = new $swoole_server($ip,$port);

        $this->server->set($this->config['set']);

        $this->server->on('start', [$this, 'onStart']);
        $this->server->on('workerstart',[$this,'onWorkerStart']);
        $this->server->on('workerstop', [$this, 'onWorkerStop']);
        $this->server->on('workererror',[$this,'onWorkerError']);
        $this->server->on('managerStart', [$this, 'onManagerStart']);
        $this->server->on('managerStop', [$this, 'onManagerStop']);

        if($this->config['server'] == 'websocket'){
            $this->server->on('open' ,[$this,'onOpen']);
            $this->server->on('message',[$this,'onMessage']);
            $this->server->on('close',[$this,'onClose']);
        }
        $this->server->on('request' ,[$this,'onRequest']);
        if( isset($this->config['set']['task_worker_num']) && $this->config['set']['task_worker_num']>0){
            $this->server->on('task',[$this,'onTask']);
            $this->server->on('finish',[$this,'onFinish']);
        }
        $this->server->start();
    }

    public function onStart($server){
        date_default_timezone_set('Asia/Shanghai');
        $this->set_process_title($this->name . '-master');
        Log::get_instance()->write('INFO',$this->name,"启动成功","{$this->config['ip']}:{$this->config['port']}");
        Hook::get_instance ()->listen('Server',$server);
    }

    /**
     * Worker进程/Task进程启动时
     */
    public function onWorkerStart($server,$workder_id){
        date_default_timezone_set('Asia/Shanghai');
        $this->workerId = $workder_id;
        if (function_exists('apc_clear_cache')) {
            apc_clear_cache();
        }
        if (function_exists('opcache_reset')) {
            opcache_reset();
        }
        // 重新加载配置
        $this->reload_config();
        if (!$server->taskworker) {//worker进程
            $this->set_process_title($this->name . "-worker");
        } else {
            $this->set_process_title($this->name . "-tasker");
        }

        //每3秒执行一次
        swoole_timer_tick(3000,function ($time_id){
            Log::get_instance ()->save();
        });
    }
    /**
     * 当worker/task_worker进程发生异常
     */
    public function onWorkerError($server, $worker_id, $worker_pid, $exit_code){
        Log::get_instance()->write('ERROR',$this->name,"进程异常","WorkerID:{$worker_id}","WorkerPID:{$worker_pid}","ExitCode:{$exit_code}");
    }
    /**
     * worker进程终止时
     * @param  $server
     * @param  $worker_id
     */
    public function onWorkerStop( $server, $worker_id){
        Log::get_instance()->write('ERROR',$this->name,"进程终止","WorkerID:{$worker_id}");
    }

    /**
     * 当管理进程启动时
     * @param $serv
     */
    public function onManagerStart($serv){
        $this->set_process_title($this->name . '-manager');
        Log::get_instance()->write('INFO',$this->name,"管理进程启动");
    }
    /**
     * 当管理进程结束时
     * @param $serv
     */
    public function onManagerStop($serv){
        Log::get_instance()->write('INFO',$this->name,"管理进程结束");
    }

    public function onOpen( $server,$request){
        Log::get_instance()->write('DEBUG',"FD:{$request->fd}","握手成功");
        Hook::get_instance ()->listen('open',$server,$request->fd);
    }

    public function onRequest($request,$response){
        if($this->config['set']['enable_static_handler'] && $request->server['request_uri'] == '/favicon.ico'){
            return ;
        }
        App::get_instance()->http($this->server,$request,$response);
    }

    public function onMessage( $server ,$frame ){
        Log::get_instance()->write('DEBUG',"FD:{$frame->fd}","Opcode:{$frame->opcode}","Finish:{$frame->finish}","Data:{$frame->data}");
        App::get_instance()->websocket($server,$frame);
    }

    public function onTask($server,$task_id,$workder_id,$data){
        return Task::get_instance ($server)->dispatch ($task_id,$workder_id,$data);
    }

    public function onFinish($server,$task_id,$data){
        Task::get_instance ($server)->finish($task_id,$data);
    }

    public function onClose($server,$fd){
        Log::get_instance()->write('DEBUG',"FD:{$fd}","关闭连接");
        Hook::get_instance ()->listen('close',$server,$fd);
    }

    /**
     * 获取 来自哪个监听端口
     * @param $fd
     * @return mixed
     */
    public function get_server_port($fd){
        return $this->server->connection_info($fd)['server_port'];
    }

    /**
     * 重新加载配置
     */
    public function reload_config(){
        $this->config = config('app');
        $this->name = $this->config['name'];
    }
    /**
     * Set process name.
     * @param string $title
     * @return void
     */
    public function set_process_title($title)
    {
        if(PHP_OS === 'Darwin')  return ;
        // >=php 5.5
        if (function_exists('cli_set_process_title')) {
            @cli_set_process_title($title);
        }else {
            @swoole_set_process_name($title);
        }
    }

}